diff options
Diffstat (limited to 'app/[lng]/admin')
| -rw-r--r-- | app/[lng]/admin/mdg/page.tsx | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/app/[lng]/admin/mdg/page.tsx b/app/[lng]/admin/mdg/page.tsx new file mode 100644 index 00000000..27416f25 --- /dev/null +++ b/app/[lng]/admin/mdg/page.tsx @@ -0,0 +1,248 @@ +'use client' + +import { useState, useEffect } from 'react' +import { Button } from '@/components/ui/button' +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { Badge } from '@/components/ui/badge' +import { toast } from 'sonner' +import { Loader2, Send, RefreshCw } from 'lucide-react' + +// CSV 필드를 정의할 타입 +interface VendorFieldDef { + table: string; + name: string; + mandatory: boolean; + description: string; +} + +// CSV 파싱 함수 (간단 파서) +const parseCSV = (csv: string): VendorFieldDef[] => { + const lines = csv.trim().split('\n'); + // 첫 번째 라인은 헤더이므로 제거 + return lines.slice(1).map((line) => { + const parts = line.split(','); + const table = parts[1]?.trim(); + const name = parts[2]?.trim(); + const mandatory = parts[3]?.trim() === 'M'; + const description = parts.slice(6).join(',').trim(); + return { table, name, mandatory, description } as VendorFieldDef; + }); +}; + +// 기존 샘플 기본값 (필요 시 확장) +const sampleDefaults: Record<string, string> = { + BP_HEADER: 'TEST001', + ZZSRMCD: 'EVCP', + TITLE: 'TEST', + BU_SORT1: 'TEST VENDOR', + NAME_ORG1: '테스트 벤더 회사', + KTOKK: 'Z001', + VEN_KFBUS: '제조업', + VEN_KFIND: 'IT', + MASTERFLAG: 'X', + IBND_TYPE: 'U', + ZZREQID: 'TESTUSER01', + ADDRNO: '0001', + AD_NATION: '1', + COUNTRY: 'KR', + LANGU_COM: 'K', + POST_COD1: '06292', + CITY1: '서울시', + DISTRICT: '강남구', + REGION: '11', + MC_STREET: '테헤란로 123', + T_COUNTRY: 'KR', + T_NUMBER: '02-1234-5678', + F_COUNTRY: 'KR', + F_NUMBER: '02-1234-5679', + U_ADDRESS: 'https://test.vendor.com', + E_ADDRESS: 'contact@test.vendor.com', + BP_TX_TYP: 'KR2', + TAXNUM: '123-45-67890', +}; + +// XML escape helper +const escapeXml = (unsafe: string) => unsafe.replace(/[<>&'"']/g, (c) => { + switch (c) { + case '<': return '<'; + case '>': return '>'; + case '&': return '&'; + case '"': return '"'; + case "'": return '''; + default: return c; + } +}); + +export default function MDGTestPage() { + const [formData, setFormData] = useState<Record<string, string>>({}); + const [fieldDefs, setFieldDefs] = useState<VendorFieldDef[]>([]); + const [resultXml, setResultXml] = useState<string>(''); + const [isLoading, setIsLoading] = useState(false); + + // CSV 로딩 및 초기 데이터 셋업 + useEffect(() => { + const load = async () => { + const res = await fetch('/wsdl/P2MD3007_AO.csv'); + const csvText = await res.text(); + const defs = parseCSV(csvText); + setFieldDefs(defs); + + const init: Record<string, string> = {}; + defs.forEach((d) => { + init[d.name] = sampleDefaults[d.name] ?? ''; + }); + setFormData(init); + }; + + load(); + }, []); + + // 폼 데이터 업데이트 + const updateField = (field: string, value: string) => { + setFormData(prev => ({ ...prev, [field]: value })); + }; + + // 기본값으로 리셋 + const resetForm = () => { + const reset: Record<string, string> = {}; + fieldDefs.forEach((d) => { + reset[d.name] = sampleDefaults[d.name] ?? ''; + }); + setFormData(reset); + toast.success('폼이 기본값으로 리셋되었습니다.'); + }; + + // 테스트 송신 실행 + const handleTestSend = async () => { + try { + setIsLoading(true); + + // 필수 필드 검증 + const requiredFields = fieldDefs.filter(d => d.mandatory).map(d => d.name); + const missingFields = requiredFields.filter(field => !formData[field]?.trim()); + + if (missingFields.length > 0) { + toast.error(`필수 필드가 누락되었습니다: ${missingFields.join(', ')}`); + return; + } + + // XML 생성 + const bodyContent = fieldDefs.map(f => { + const val = formData[f.name] ?? ''; + return `<${f.name}>${escapeXml(val)}</${f.name}>`; + }).join('\n '); + + const supplierXml = `<SUPPLIER_MASTER>\n ${bodyContent}\n </SUPPLIER_MASTER>`; + + const envelope = `<?xml version="1.0" encoding="UTF-8"?>\n<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:p1=\"http://shi.samsung.co.kr/P2_MD/MDZ\">\n <soap:Header/>\n <soap:Body>\n <p1:MT_P2MD3007_S>\n <P2MD3007_S>\n ${supplierXml}\n </P2MD3007_S>\n </p1:MT_P2MD3007_S>\n </soap:Body>\n</soap:Envelope>`; + + setResultXml(envelope); + toast.success('요청 XML이 생성되었습니다. 하단 영역을 확인하세요.'); + + } catch (error) { + console.error('테스트 송신 실패:', error); + toast.error('테스트 송신 중 오류가 발생했습니다.'); + } finally { + setIsLoading(false); + } + }; + + return ( + <div className="container mx-auto p-6 space-y-6"> + <div className="flex items-center justify-between"> + <div> + <h1 className="text-3xl font-bold">MDG VENDOR 마스터 테스트</h1> + <p className="text-muted-foreground mt-2"> + VENDOR 마스터 데이터를 MDG 시스템으로 테스트 송신합니다 + </p> + </div> + <div className="flex gap-2"> + <Button variant="outline" onClick={resetForm}> + <RefreshCw className="w-4 h-4 mr-2" /> + 리셋 + </Button> + <Button onClick={handleTestSend} disabled={isLoading}> + {isLoading ? ( + <Loader2 className="w-4 h-4 mr-2 animate-spin" /> + ) : ( + <Send className="w-4 h-4 mr-2" /> + )} + 테스트 송신 + </Button> + </div> + </div> + + {/* 동적 필드 렌더링 */} + {fieldDefs.length === 0 ? ( + <p className="text-center text-muted-foreground">CSV 로딩 중...</p> + ) : ( + <div className="space-y-6"> + {Object.entries( + fieldDefs.reduce((acc: Record<string, VendorFieldDef[]>, cur) => { + acc[cur.table] = acc[cur.table] ? [...acc[cur.table], cur] : [cur]; + return acc; + }, {}) + ).map(([table, fields]) => ( + <Card key={table}> + <CardHeader> + <CardTitle className="flex items-center gap-2"> + {table} + {fields.some(f => f.mandatory) && ( + <Badge variant="destructive">필수 포함</Badge> + )} + </CardTitle> + <CardDescription>{table} 테이블 입력</CardDescription> + </CardHeader> + <CardContent className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> + {fields.filter((f, idx, arr) => arr.findIndex(x => x.name === f.name) === idx).map((field) => ( + <div key={field.name}> + <Label htmlFor={field.name} className="flex items-center gap-1"> + {field.name} + {field.mandatory && ( + <Badge variant="destructive" className="ml-1">필수</Badge> + )} + </Label> + <Input + id={field.name} + value={formData[field.name] ?? ''} + onChange={(e) => updateField(field.name, e.target.value)} + /> + {field.description && ( + <p className="text-xs text-muted-foreground mt-1">{field.description}</p> + )} + </div> + ))} + </CardContent> + </Card> + ))} + </div> + )} + + {/* 송신 결과 영역 */} + <Card> + <CardHeader> + <CardTitle>송신 결과</CardTitle> + <CardDescription> + MDG 시스템으로의 송신 결과가 여기에 표시됩니다 + </CardDescription> + </CardHeader> + <CardContent> + {resultXml ? ( + <pre className="p-4 bg-muted max-h-96 overflow-auto text-xs whitespace-pre-wrap"> + {resultXml} + </pre> + ) : ( + <div className="p-4 bg-muted rounded-lg"> + <p className="text-sm text-muted-foreground"> + 테스트 송신 버튼을 클릭하면 결과가 표시됩니다. + </p> + </div> + )} + </CardContent> + </Card> + </div> + ); +} + |
